//-----------------------------------------------------------
//
//-----------------------------------------------------------
class ZFPawn extends ROPawn;

function DoDamageFX( Name boneName, int Damage, class<DamageType> DamageType, Rotator r )
{
    if( (Health < 0 && Damage > DamageType.default.HumanObliterationThreshhold && Damage != 1000) || (DamageType == class'PTRDDamType'&&boneName!='head'))
	{
		boneName = 'obliterate';
	}
	else if( Health <= 0 && boneName != 'head' )
	{
        DoDamageFX('head',1000,DamageType,r);
	}

    HitFX[HitFxTicker].damtype = DamageType;
    HitFX[HitFxTicker].bSever = true; // Zombie Front: Always severs :3
    HitFX[HitFxTicker].bone = boneName;
    HitFX[HitFxTicker].rotDir = r;
    HitFxTicker = HitFxTicker + 1;
    if( HitFxTicker > ArrayCount(HitFX)-1 )
        HitFxTicker = 0;
}

simulated function ProcessHitFX()
{
    local Coords boneCoords;
    local int j;
    local float GibPerterbation;

    if( (Level.NetMode == NM_DedicatedServer) )
    {
		SimHitFxTicker = HitFxTicker;
        return;
    }

    for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
    {
		j++;
		if ( j > 30 )
		{
			SimHitFxTicker = HitFxTicker;
			return;
		}

        if( (HitFX[SimHitFxTicker].damtype == None) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
            continue;

		if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
		{
			SpawnGibs( HitFX[SimHitFxTicker].rotDir, 0);
			bGibbed = true;
			Destroy();
			return;
		}

        boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );

        if ( !Level.bDropDetail )
        {
			AttachEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
		}

        if( HitFX[SimHitFxTicker].bSever )
        {
            GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;

            switch( HitFX[SimHitFxTicker].bone )
            {
                case 'obliterate':
                    break;

				case 'lthigh':
                case 'lupperthigh':
                	if( !bLeftLegGibbed )
					{
	                    SpawnGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
	                    bLeftLegGibbed=true;
                    }
                    break;

                case 'rthigh':
                case 'rupperthigh':
                	if( !bRightLegGibbed )
					{
	                    SpawnGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
	                    bRightLegGibbed=true;
                    }
                    break;

                case 'lfarm':
                case 'lupperarm':
                	if( !bLeftArmGibbed )
					{
	                    SpawnGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
	                    bLeftArmGibbed=true;
                    }
                    break;

                case 'rfarm':
                case 'rupperarm':
                	if( !bRightArmGibbed )
					{
	                    SpawnGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
	                    bRightArmGibbed=true;
                    }
                    break;
            }

			if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != 'UpperSpine' )
            	HideBone(HitFX[SimHitFxTicker].bone);
        }

        if(HitFX[SimHitFXTicker].bone == 'head')
        {
        	if( Headgear != none )
        	{
        		HelmetShotOff(HitFX[SimHitFxTicker].rotDir);
        	}
        }
    }
}

simulated function HideBone(name boneName)
{
	local int BoneScaleSlot;

    if( boneName == 'lfoot' )
    {
		boneScaleSlot = 0;
	}
    else if( boneName == 'lthigh' )
    {
		boneScaleSlot = 1;
		if( SeveredLeftLeg == none )
		{
			SeveredLeftLeg = Spawn(SeveredLegAttachClass,self);
			AttachToBone(SeveredLeftLeg, 'lupperthigh');
		}
	}
	else if( boneName == 'lupperthigh' )
    {
		boneScaleSlot = 2;
		if( SeveredLeftLeg != none )
		    SeveredLeftLeg.Destroy();
	}
	else if( boneName == 'rfoot' )
    {
		boneScaleSlot = 3;
	}
	else if ( boneName == 'rthigh' )
	{
		boneScaleSlot = 4;
		if( SeveredRightLeg == none )
		{
			SeveredRightLeg = Spawn(SeveredLegAttachClass,self);
			AttachToBone(SeveredRightLeg, 'rupperthigh');
		}
	}
	else if( boneName == 'rupperthigh' )
    {
		boneScaleSlot = 5;
		if( SeveredRightLeg != none )
		    SeveredRightLeg.Destroy();

	}
	else if( boneName == 'rhand' )
	{
		boneScaleSlot = 6;
	}
	else if( boneName == 'rfarm' )
	{
		boneScaleSlot = 7;
		if( SeveredRightArm == none )
		{
			SeveredRightArm = Spawn(SeveredArmAttachClass,self);
			AttachToBone(SeveredRightArm, 'rupperarm');
		}
	}
	else if( boneName == 'rupperarm' )
	{
		boneScaleSlot = 8;
		if( SeveredRightArm != none )
		    SeveredRightArm.Destroy();
	}
	else if ( boneName == 'lhand' )
	{
		boneScaleSlot = 9;
	}
	else if ( boneName == 'lfarm' )
	{
		boneScaleSlot = 10;
		if( SeveredLeftArm == none )
		{
			SeveredLeftArm = Spawn(SeveredArmAttachClass,self);
			AttachToBone(SeveredLeftArm, 'lupperarm');
		}
	}
	else if ( boneName == 'lupperarm' )
	{
		boneScaleSlot = 11;
		if( SeveredLeftArm != none )
		    SeveredLeftArm.Destroy();
	}
	else if ( boneName == 'head' )
	{
		boneScaleSlot = 12;
		if( SeveredHead == none )
		{
			SeveredHead = Spawn(SeveredHeadAttachClass,self);
			AttachToBone(SeveredHead, 'Bip01_neck');
		}
	}
	else
    {
       boneName = 'spine';
   	   BoneScaleSlot = 13;
	}

    SetBoneScale(BoneScaleSlot, 0.0, BoneName);
}

function TakeDamage(int Damage, Pawn InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional int HitIndex)
{
	local int actualDamage;
	local Controller Killer;

    //__________________________________________
    // Error Checking --------------------------
	if ( damagetype == None )
	{
		if ( InstigatedBy != None )
			warn("No damagetype for damage by "$instigatedby$" with weapon "$InstigatedBy.Weapon);
		DamageType = class'DamageType';
	}
	if ( Role < ROLE_Authority )
		return; // Only servers or standalones have the powa to damage!
	if ( Health <= 0 )
		return; // Dude, they are already dead, stop already!
	if ((instigatedBy == None || instigatedBy.Controller == None) && DamageType.default.bDelayedDamage && DelayedDamageInstigatorController != None)
		instigatedBy = DelayedDamageInstigatorController.Pawn; // Don't even know what this does...

    //__________________________________________
    // Physics Jazz, (explosions push you) -----
	if ( (Physics == PHYS_None) && (DrivenVehicle == None) )
		SetMovementPhysics();
	if (Physics == PHYS_Walking && damageType.default.bExtraMomentumZ)
		momentum.Z = FMax(momentum.Z, 0.4 * VSize(momentum));
	if ( instigatedBy == self )
		momentum *= 0.6;
	momentum = momentum/Mass;

    //__________________________________________
    // Damage Modification ---------------------
	if (Weapon != None)
		Weapon.AdjustPlayerDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType );
	if (DrivenVehicle != None)
	    DrivenVehicle.AdjustDriverDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType );
	ActualDamage = Level.Game.ReduceDamage(Damage, self, instigatedBy, HitLocation, Momentum, DamageType);
	if (ActualDamage > 0 && (DamageType.Name == 'Fell'))
		SetLimping(FMin(ActualDamage / 5.0, 10.0));
	ZombieReduceDamage(ActualDamage,DamageType,HitIndex);

    //__________________________________________
    // Actually Damage the Player --------------
	Health -= actualDamage;
	if ( HitLocation == vect(0,0,0) )
		HitLocation = Location;

    //__________________________________________
    // Special FX and Damage Reporting ---------
	PlayHit(actualDamage,InstigatedBy, hitLocation, damageType, Momentum, HitIndex);
	if ( Health <= 0 )
	{
		// pawn died
		if ( DamageType.default.bCausedByWorld && (instigatedBy == None || instigatedBy == self) && LastHitBy != None )
			Killer = LastHitBy;
		else if ( instigatedBy != None )
			Killer = instigatedBy.GetKillerController();
		if ( Killer == None && DamageType.Default.bDelayedDamage )
			Killer = DelayedDamageInstigatorController;
		if ( bPhysicsAnimUpdate )
			SetTearOffMomemtum(momentum);
		Died(Killer, damageType, HitLocation);
	}
	else
	{
		AddVelocity( momentum );
		if ( Controller != None )
			Controller.NotifyTakeHit(instigatedBy, HitLocation, actualDamage, DamageType, Momentum);

		if ( instigatedBy != None && instigatedBy != self )
			LastHitBy = instigatedBy.Controller;
	}
	MakeNoise(1.0);
}

function ZombieReduceDamage(out int Damage, class<DamageType> DamageType, int HitIndex)
{
    local Name HitBone;

    if( DamageType.default.bLocationalHit && DamageType.default.Name != 'Fell' )
    {
        HitBone = GetHitBoneFromIndex(HitIndex);

        if ( HitBone == 'head' || DamageType == class'PTRDDamType')
            Damage = 100; // 150 just incase
        else
            Damage = 1;

        log("Hit bone:"@HitBone@"Damage:"@Damage);
    }
    else
    {
        if ( Damage >= 50 )
            Damage = 100;
        else
            Damage = 1;

        log("Non-locational Damage:"@Damage);
    }
}

function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIndex)
{
    local Vector HitNormal, BloodOffset, Mo;
    local Name HitBone;
	local ProjectileBloodSplat BloodHit;
	local rotator SplatRot;
    local class<Effects> DesiredEffect;
   	local class<Emitter> DesiredEmitter;
	local PlayerController Hearer;

    if( DamageType.default.bLocationalHit )
    {
        HitBone = GetHitBoneFromIndex(HitIndex);
    }
    else
    {
        HitLocation = Location;
        HitBone = 'None';
    }

    if( DamageType.default.bAlwaysSevers && DamageType.default.bSpecial )
        HitBone = 'head';

    //__________________________________________
    // Make a bloody mess ----------------------
    //__________________________________________
    // Epic's mess code  -----------------------
    HitNormal = Normal(HitLocation - Location);
	// Play any set effect
	if ( EffectIsRelevant(Location,true) )
	{
		DesiredEffect = DamageType.static.GetPawnDamageEffect(HitLocation, Damage, Momentum, self, (Level.bDropDetail || Level.DetailMode == DM_Low));

		if ( DesiredEffect != None )
		{
			BloodOffset = 0.2 * CollisionRadius * HitNormal;
			BloodOffset.Z = BloodOffset.Z * 0.5;

			Mo = Momentum;
			if ( Mo.Z > 0 )
				Mo.Z *= 0.5;
			spawn(DesiredEffect,self,,HitLocation + BloodOffset, rotator(Mo));
		}
		DesiredEmitter = DamageType.Static.GetPawnDamageEmitter(HitLocation, Damage, Momentum, self, (Level.bDropDetail || Level.DetailMode == DM_Low));
		if (DesiredEmitter != None)
		{
		    if( InstigatedBy != none )
		        HitNormal = Normal((InstigatedBy.Location+(vect(0,0,1)*InstigatedBy.EyeHeight))-HitLocation);

			spawn(DesiredEmitter,,,HitLocation+HitNormal, Rotator(HitNormal));
		}
	}
    //__________________________________________
    // Tripwire's Bloody Mess Code -------------
    if ( Momentum != vect(0,0,0) )
		SplatRot = rotator(Normal(Momentum));
	else
	{
		if ( InstigatedBy != None )
			SplatRot = rotator(Normal(Location - InstigatedBy.Location));
		else
			SplatRot = rotator(Normal(Location - HitLocation));
	}
 	BloodHit = Spawn(ProjectileBloodSplatClass,InstigatedBy,, HitLocation, SplatRot);

 	//__________________________________________
 	// Epic's Ouch Sound effects! --------------
 	if ( Level.TimeSeconds - LastPainTime > 0.1 )
	{
		if ( InstigatedBy != None && (DamageType != None) && DamageType.default.bDirectDamage )
			Hearer = PlayerController(InstigatedBy.Controller);
		if ( Hearer != None )
			Hearer.bAcuteHearing = true;
		PlayTakeHit(HitLocation,100,damageType); // Zombie Front: 100 for full ouchies!
		if ( Hearer != None )
			Hearer.bAcuteHearing = false;
		LastPainTime = Level.TimeSeconds;
	}

    //__________________________________________
    // Limbs ripping off
    // Hitnormal is speciying where the damage
    // came from ------------------------------
	if( InstigatedBy != None )
		HitNormal = Normal( Normal(InstigatedBy.Location-HitLocation) + VRand() * 0.2 + vect(0,0,2.8) );
	else
		HitNormal = Normal( Vect(0,0,1) + VRand() * 0.2 + vect(0,0,2.8) );
	DoDamageFX( HitBone, Damage, DamageType, Rotator(HitNormal) );

	if (DamageType.default.DamageOverlayMaterial != None && Damage > 0 )
	    SetOverlayMaterial( DamageType.default.DamageOverlayMaterial, DamageType.default.DamageOverlayTime, false );
}

// Zombies can't run!
function SetSprinting(bool bNewIsSprinting)
{
    bIsSprinting=false;
}

defaultproperties
{
	Mesh=SkeletalMesh'ZF_Characters_prototype_anm.zf_prototype_zombie_rig'
	Skins(0)=Texture'zf_characters_prototype_T.uniforms.ZF_zombie_prototype_tanker'
	Skins(1)=Texture'zf_characters_prototype_T.Faces.ZFGermanface01'
	Species=Class'ZFCore.ZFSPECIES_Zombie'

	MovementAnims(0)=zf_stand_walk_f
	MovementAnims(1)=zf_stand_walk_b
	MovementAnims(2)=zf_stand_walk_l
	MovementAnims(3)=zf_stand_walk_r
	MovementAnims(4)=zf_stand_walk_f // front left
	MovementAnims(5)=zf_stand_walk_f // front right
	MovementAnims(6)=zf_stand_walk_b // back left
	MovementAnims(7)=zf_stand_walk_b // back right

    bDramaticLighting=false

	PlacedCharacterName="Zombie"
	AccelRate=300.0
	SprintAccelRate=350.0
	GroundSpeed=75.000000
	WaterSpeed=10.000000
	LadderSpeed=0.0
	AirSpeed=70.000000
	JumpZ=200.000000
	WalkingPct=0.350000
	CrouchedPct=0.30000
	MaxFallSpeed=650.0//600.000000
	CrouchHeight=38.0//34.0
	CrouchRadius=22.0
	HeadScale=1.000000
    ControllerClass=Class'ZFCore.ZFBot'

    SeveredHeadAttachClass = class'ZFSeveredHead'
}
